#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include "Fpga_Define.h"
#include "Fpga.h"
/******************************************************/
// 功能： 延时函数，单位 us
/******************************************************/
void delay(int usec) // 延时函数微秒为单位
{
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = usec;
    select(0, NULL, NULL, NULL, &tv);
}
/******************************************************/
// 功能： 打开 FPGA, 并完成内存映射
/******************************************************/
int fd;
unsigned short *fpga_buf;
unsigned short first, second;
void Open_FPGA()
{
    fd = open(DEVFILE, O_RDWR);
    fpga_buf = (unsigned short *)mmap(NULL, FPGA_SZ, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // 这里用到驱动里面的 fpga_mmap 函数
    printf("FPGA Device Open!\n");
}
/******************************************************/
// 功能： 关闭设备
/******************************************************/
void Close_FPGA()
{
    munmap(fpga_buf, FPGA_SZ);
    close(fd);
    printf("FPGA Device Closed!\n");
}
/******************************************************/
// 功能： FPGA 初始化
/******************************************************/
void FPGA_Init()
{
    RST_High();
    delay(1000);
    RST_Low();
}
/******************************************************/
// 功能： 往一个中间文件里写入下载进度
/******************************************************/
void Write_Download_Process(unsigned int Progress_Status)
{
    FILE *fp_PStatus;
    fp_PStatus = fopen("Download.tmp", "wb");
    fwrite((unsigned char *)(&Progress_Status), sizeof(unsigned int), 1, fp_PStatus); // 往中间文件写入下载百分比
    fclose(fp_PStatus);
}
/******************************************************/
// 功能： bit 反转
/******************************************************/
unsigned char reversebit(register unsigned char x)
{
    register unsigned char y = 0x55;
    x = (((x >> 1) & y) | ((x & y) << 1));
    y = 0x33;
    x = (((x >> 2) & y) | ((x & y) << 2));
    return ((x >> 4) | (x << 4));
}
/******************************************************/
// 功能： 按字节反转
/******************************************************/
U32 reverse(register U32 x)
{
    unsigned char x3 = ((x >> (8 * 3)) & 0xff);
    unsigned char x2 = ((x >> (8 * 2)) & 0xff);
    unsigned char x1 = ((x >> (8 * 1)) & 0xff);
    unsigned char x0 = ((x >> (8 * 0)) & 0xff);
    x3 = reversebit(x3);
    x2 = reversebit(x2);
    x1 = reversebit(x1);
    x0 = reversebit(x0);
    return ((U32)x3 << (8 * 3)) | ((U32)x2 << (8 * 2)) | ((U32)x1 << (8 * 1)) | (U32)x0;
}

/******************************************************/
// 功能： 下载 Bin 文件
// 参数： filename--Bin 文件的文件名
// 返回值：1-- 下载成功；0-- 下载失败
/******************************************************/
char FPGA_Download(int sock, const char *fileName)
{
    FILE *fp;
    unsigned char Progress_Status = 0;
    struct stat info;
    if (stat(fileName, &info) == -1)
    {
        printf("FileName Error:%s\n", strerror(errno));
        return -1;
    }
    if (!S_ISREG(info.st_mode))
    {
        printf("%s is not a binary file!\n", fileName);
        return -1;
    }
    if ((fp = fopen(fileName, "rb")) == NULL)
    {
        printf("Failed to Open %s\n", fileName);
        return -1;
    }
    int FileLen, FilePos;
    U32 data[1024];
    int readnum = 1024;

    FileLen = info.st_size;
    printf("FileLen = %d\n", FileLen);
    // 重置 FPGA
    CFG_CCLK_High();
    delay(100);
    CFG_PROB_Low();
    delay(100);
    CFG_PROB_High();
    volatile int rr;
    do
    {
        delay(20);
        rr = CFG_INIT();
    } while (rr == 0);
    delay(20);
    // 开始下载
    CFG_CCLK_Low();
    CFG_DIN_Low();
    delay(20);
    int wait = 0;
    int startdownload = 0;
    int syncwordaa995566 = 0;
    int syncword5599aa66 = 0;
    int reversestate = 0;
    while (!feof(fp))
    {
        readnum = fread(&data, sizeof(U32), readnum, fp);
        FilePos = ftell(fp);
        fflush(stdout);
        if (startdownload == 1)
        {
            Progress_Status = FilePos * 100 / FileLen;
            if (reversestate == 1)
            {
                for (int i = 0; i < readnum; i++)
                {
                    data[i] = reverse(data[i]);
                }
            }
            write(fd, (char *)data, sizeof(U32) * readnum);
        }
        if (startdownload == 0)
        {
            for (int i = 0; i < readnum; i++)
            {
                //Check bin file sync word
                if ((i < readnum))
                {
                    printf("%x\n", data[i]);
                    printf("%x\n", reverse(data[i]));
                    syncwordaa995566 = (data[i] == 0x665599aa);
                    syncword5599aa66 = (data[i] == 0x66aa9955);
                }
                if (syncwordaa995566 == 1)
                {
                    reversestate = 1;
                    startdownload = 1;
                    fseek(fp, 0, SEEK_SET);
                    break;
                }
                else if (syncword5599aa66 == 1)
                {
                    reversestate = 0;
                    startdownload = 1;
                    fseek(fp, 0, SEEK_SET);
                    break;
                }
            }
        }
        if (startdownload == 0)
        {
            printf("Sync word missing!\n");
            return -1;
        }
        wait++;
        if (wait == 100)
        {
            wait = 0;
            send(sock, (char *)(&Progress_Status), sizeof(Progress_Status), 0);
            //Write_Download_Process(Progress_Status);
            printf("%d\n", Progress_Status);
        }
        if (CFG_INIT() == 0)
        {
            printf("Download error!");
        }
    }
    // 下载完毕
    delay(100);
    int done = CFG_DONE();
    if (done)
    {
        printf("\nFPGA Download Successed!\n");
        Write_Download_Process(100);
    }
    else
    {
        printf("\nFPGA Download Failed!\n");
        Write_Download_Process(0);
    }
    fclose(fp);
    return done;
}
/******************************************************/
// 功能：从 FPGA 内存读取 2 字节数据
// 参数：offset-- 读地址
/******************************************************/
unsigned short FPGA_ReadShort_Fast(unsigned int offset)
{
    unsigned int addr = 0x40000 + offset;
    return fpga_buf[addr];
}
/******************************************************/
// 功能：从 FPGA 内存读取 2 字节数据，并确认
// 参数：offset-- 读地址
/******************************************************/
unsigned short FPGA_ReadShort(unsigned int offset)
{
    unsigned int addr = 0x40000 + offset;
    first = fpga_buf[addr];
    second = fpga_buf[addr];
    int count = 0;
    while (first != second)
    {
        if (first == 65535 || first == 49150 || first == 49118 || first == 49151 || first == 47070 || first == 65534)
        {
            if (count > 5)
            {
                second = 65535;
                break;
            }
            else
            {
                count++;
                first = fpga_buf[addr];
                second = fpga_buf[addr];
            }

        }
        else
        {
            first = fpga_buf[addr];
            second = fpga_buf[addr];
        }
    }
    return second;
}
/******************************************************/
// 功能：向 FPGA 内存写入 2 字节数据
// 参数：offset-- 写地址；data-- 写入的数据
/******************************************************/
void FPGA_WriteShort(unsigned int offset, unsigned short data)
{
    unsigned int addr = 0x40000 + offset;
    fpga_buf[addr] = data;
}
